package com.cloorc.example.cloud.service;

import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;

public class RequestMappingHandlerMappingExtended extends RequestMappingHandlerMapping {
    public RequestMappingHandlerMappingExtended() {
    }

    protected RequestMappingInfo getMappingForMethodNew(Method method, Class<?> handlerType) {
        RequestMappingInfo info = createRequestMappingInfoNew(method);
        if (info != null) {
            RequestMappingInfo typeInfo = createRequestMappingInfoNew(handlerType);
            if (typeInfo != null) {
                info = typeInfo.combine(info);
            }
        }
        return info;
    }

    private RequestMappingInfo createRequestMappingInfoNew(AnnotatedElement element) {
        final Queue<AnnotatedElement> elements = new LinkedList<>();
        elements.add(element);
        RequestMapping requestMapping = null;
        AnnotatedElement ae;
        while (null == requestMapping && null != (ae = elements.poll())) {
            requestMapping =
                AnnotatedElementUtils.findMergedAnnotation(ae, RequestMapping.class);
            if (null == requestMapping) {
                if (ae instanceof Class) {
                    if (null != ((Class) ae).getSuperclass() &&
                        !Object.class.equals(((Class) ae).getSuperclass()))
                        elements.add(((Class) ae).getSuperclass());
                    elements.addAll(Arrays.asList(((Class) ae).getInterfaces()));
                } else if (ae instanceof Method) {
                    Method method = (Method) ae;
                    Class declaring = ((Method) ae).getDeclaringClass();
                    List<Class> superClasses = new ArrayList<>();
                    superClasses.add(declaring);
                    superClasses.addAll(Arrays.asList(declaring.getInterfaces()));
                    for (Class superClass : superClasses) {
                        Method candidate = null;
                        try {
                            candidate = superClass.getMethod(method.getName(),
                                method.getParameterTypes());
                            if (null != candidate)
                                elements.add(candidate);
                        } catch (NoSuchMethodException ignored) {
                        }
                    }
                }
            }
        }

        RequestCondition<?> condition = (element instanceof Class<?> ?
            getCustomTypeCondition((Class<?>) element) :
            getCustomMethodCondition((Method) element));
        return (requestMapping != null ?
            createRequestMappingInfo(requestMapping, condition) : null);
    }

    @Override
    public void registerMapping(RequestMappingInfo mapping, Object handler, Method method) {
        super.registerMapping(mapping, handler, method);
        final String name = getNamingStrategy().getName(createHandlerMethod(handler, method), mapping);
        final HandlerMethod hm = (HandlerMethod) getHandlerMethodsForMappingName(name);
        final MethodParameter[] parameters = hm.getMethodParameters();
        if (null != parameters) {
            for (MethodParameter mp : parameters) {
                final Annotation[] annotations = mp.getParameterAnnotations();
                for (Annotation annotation : annotations) {
                    if (annotation.getClass().getName().startsWith(RequestMapping.class.getPackage().getName()))
                        return;
                }
            }
        }
    }
}
